import { svgPathProperties } from 'svg-path-properties'
import { PathPoint, Point, RouteObject, TrafficNetMethod } from './trafficNetType'
import { generateUUID } from '../../utils'
import Canvas from '../canvas/canvas'

class TrafficNetwork {
  private _trafficGraph: RouteObject = {}

  //初始化交通网络
  private parsePathWithAccurateT(pathData: string): PathPoint[] {
    const props = new svgPathProperties(pathData)
    const segments: PathPoint[] = []

    // 获取路径采样点，用于计算累积长度
    const totalSamples = 500
    const samplePoints: { x: number; y: number; length: number }[] = []

    let totalLength = 0
    let last = props.getPointAtLength(0)
    samplePoints.push({ ...last, length: 0 })

    for (let i = 1; i <= totalSamples; i++) {
      const p = props.getPointAtLength((i / totalSamples) * props.getTotalLength())
      const d = Math.hypot(p.x - last.x, p.y - last.y)
      totalLength += d
      samplePoints.push({ ...p, length: totalLength })
      last = p
    }

    // 查找最接近某个点在路径中的累积长度
    function findT(x: number, y: number): number {
      let minDist = Infinity
      let bestLength = 0
      for (const p of samplePoints) {
        const dist = Math.hypot(p.x - x, p.y - y)
        if (dist < minDist) {
          minDist = dist
          bestLength = p.length
        }
      }
      return +(bestLength / totalLength).toFixed(6)
    }

    // 解析 path 命令，提取关键点
    const commands = pathData.match(/[a-df-z][^a-df-z]*/gi) || []
    let current = { x: 0, y: 0 }

    for (const cmd of commands) {
      const type = cmd[0]
      const nums = cmd
        .slice(1)
        .trim()
        .split(/[\s,]+/)
        .map(Number)
      if (type.toLowerCase() === 'm' || type.toLowerCase() === 'l') {
        for (let i = 0; i < nums.length; i += 2) {
          const [x, y] = [nums[i], nums[i + 1]]
          current = { x, y }
          segments.push({ x, y, type: type === 'M' ? 'moveto' : 'lineto', t: findT(x, y) })
        }
      } else if (type.toLowerCase() === 'c') {
        for (let i = 0; i < nums.length; i += 6) {
          const [x1, y1, x2, y2, x, y] = nums.slice(i, i + 6)
          segments.push({ x: x1, y: y1, type: 'control1', t: findT(x1, y1) })
          segments.push({ x: x2, y: y2, type: 'control2', t: findT(x2, y2) })
          segments.push({ x, y, type: 'curveto', t: findT(x, y) })
          current = { x, y }
        }
      }
    }

    return segments
  }
  private getPointByAt(
    path: string,
    t: number
  ): {
    x: number
    y: number
    t: number
  } {
    if (t < 0 || t > 1) {
      throw new Error('参数 t 必须在 0 到 1 之间')
    }

    const properties = new svgPathProperties(path)
    const totalLength = properties.getTotalLength()

    // 获取指定位置的坐标点
    const point = properties.getPointAtLength(totalLength * t)

    return {
      x: point.x,
      y: point.y,
      t
    }
  }
  public initTrafficNet() {
    const canvas = Canvas.getCanvas()

    //过滤出所有十字路口的节点
    const crossingNodes = canvas.nodes.filter((node) => node.shape === 'cross-node')

    //过滤出所有的道路
    const roadEdges = canvas.edges.filter((edge) => edge.shape === 'road-edge')

    //返回结果
    const edgeResult = [] as { id: string; sourceId: string; targetId: string; data: any }[]

    //遍历所有的道路
    roadEdges.forEach((edge) => {
      //获取path的所有端点
      const path = edge.simData?.path || ''
      // 这是path的所有点位
      const pathPoints = this.parsePathWithAccurateT(path)

      /**
       * 获取所有用户自定义的点位
       */
      const labels = edge.labels || []
      const labelPoints = labels.map((item) => {
        return this.getPointByAt(path, item.distance)
      })

      const allPoints = [...pathPoints, ...labelPoints].sort((p1, p2) => p1.t - p2.t)

      let sourceId = ''
      let targetId = ''
      const graphRouting = {} as any
      //是一个元素的id
      let preUUID = ''
      allPoints.forEach((point, index) => {
        const uuid = generateUUID()
        if (index > 0) {
          graphRouting[uuid] = {
            position: [point.x, point.y],
            route: [preUUID]
          }

          graphRouting[preUUID].route.push(uuid)
        } else {
          graphRouting[uuid] = {
            position: [point.x, point.y],
            route: []
          }
        }

        if (index === 0 && typeof edge.sourceId === 'string') {
          graphRouting[uuid].route.push(edge.sourceId)
        }
        if (index === allPoints.length - 1 && typeof edge.targetId === 'string') {
          graphRouting[uuid].route.push(edge.targetId)
        }

        if (index === 0) {
          sourceId = uuid
        }
        if (index === allPoints.length - 1) {
          targetId = uuid
        }
        preUUID = uuid
      })

      edgeResult.push({
        id: edge.id,
        sourceId: sourceId,
        targetId: targetId,
        data: graphRouting
      })
    })

    const nodeResult = {} as any

    //遍历所有的十字路
    crossingNodes.forEach((node) => {
      //整理输入
      const inputEdges = roadEdges.filter((edge) => edge.targetId === node.id)
      const inputEdgeIds = inputEdges.map((item) => item.id)
      //整理输出
      const outputEdges = roadEdges.filter((edge) => edge.sourceId === node.id)
      const outputEdgeIds = outputEdges.map((item) => item.id)

      const inputRoute = inputEdgeIds.map((id) => {
        return edgeResult.find((item) => item.id === id)?.targetId
      })
      const outputRoute = outputEdgeIds.map((id) => {
        return edgeResult.find((item) => item.id === id)?.sourceId
      })

      const totalRoute = [...inputRoute, ...outputRoute]

      nodeResult[node.id] = {
        position: [node.x + 60, node.y + 60],
        route: totalRoute
      }
    })

    const result = edgeResult
      .map((item) => {
        return item.data
      })
      .concat(nodeResult)

    // 合并两个数据源
    const mergeRouteData = (data: RouteObject[]): RouteObject => {
      return data.reduce((acc, curr) => {
        // 将当前对象的每个元素合并到结果中
        for (const key in curr) {
          if (curr.hasOwnProperty(key)) {
            acc[key] = curr[key]
          }
        }
        return acc
      }, {} as RouteObject)
    }

    // 调用合并函数
    this._trafficGraph = mergeRouteData(result)

    return this._trafficGraph
  }

  //获取交通网络
  public getTrafficNet() {
    return this._trafficGraph
  }

  // 获取两个点之间的距离（这里使用欧几里得距离作为启发式估算）
  private calculateDistance(position1: Point, position2: Point): number {
    const dx = position2[0] - position1[0]
    const dy = position2[1] - position1[1]
    return Math.sqrt(dx * dx + dy * dy) // 欧几里得距离
  }

  //根据当前位置获取到最近的点位
  public getAdjacentPoints(target: Point) {
    let nearestId: string | null = null
    let minDistance = Infinity

    for (const id in this._trafficGraph) {
      const node = this._trafficGraph[id]
      const dist = this.calculateDistance(target, node.position)
      if (dist < minDistance) {
        minDistance = dist
        nearestId = id
      }
    }

    if (nearestId) {
      return { id: nearestId, distance: minDistance }
    } else {
      return null
    }
  }

  //根据id获取到位置
  public getPositionById(id: string) {
    return this._trafficGraph[id]?.position
  }

  //获取到最优路径
  public getOptimalPath(startId: string, targetId: string) {
    const openList: Set<string> = new Set()
    const closedList: Set<string> = new Set()
    const gScores: { [key: string]: number } = {}
    const fScores: { [key: string]: number } = {}
    const cameFrom: { [key: string]: string | null } = {}

    // 初始化
    for (const nodeId in this._trafficGraph) {
      if (this._trafficGraph.hasOwnProperty(nodeId)) {
        gScores[nodeId] = Infinity
        fScores[nodeId] = Infinity
        cameFrom[nodeId] = null
        openList.add(nodeId)
      }
    }
    gScores[startId] = 0
    fScores[startId] = this.calculateDistance(
      this._trafficGraph[startId].position,
      this._trafficGraph[targetId].position
    )

    while (openList.size > 0) {
      // 从 openList 中选择 f 值最小的节点
      let currentId = ''
      let lowestFScore = Infinity
      for (const nodeId of openList) {
        if (fScores[nodeId] < lowestFScore) {
          lowestFScore = fScores[nodeId]
          currentId = nodeId
        }
      }

      if (currentId === targetId) {
        // 找到目标节点，重建路径
        const path: string[] = []
        let current = targetId
        while (current !== null) {
          path.unshift(current)
          current = cameFrom[current]!
        }
        return path
      }

      // 从 openList 移除当前节点并加入到 closedList
      openList.delete(currentId)
      closedList.add(currentId)

      // 遍历邻居节点
      const currentNode = this._trafficGraph[currentId]
      for (const neighborId of currentNode.route) {
        if (closedList.has(neighborId)) {
          continue // 如果该邻居已在 closedList 中，跳过
        }

        if (!this._trafficGraph[neighborId]?.position) {
          new Error('搬运设备未找到对应点位')
        }

        const tentativeGScore =
          gScores[currentId] +
          this.calculateDistance(currentNode.position, this._trafficGraph[neighborId].position)

        if (!openList.has(neighborId)) {
          openList.add(neighborId)
        }

        if (tentativeGScore < gScores[neighborId]) {
          cameFrom[neighborId] = currentId
          gScores[neighborId] = tentativeGScore
          fScores[neighborId] =
            gScores[neighborId] +
            this.calculateDistance(
              this._trafficGraph[neighborId].position,
              this._trafficGraph[targetId].position
            )
        }
      }
    }

    // 如果 openList 为空，表示没有路径
    return []
  }

  public executeMethod<M extends TrafficNetMethod>(
    methodName: M,
    ...args: Parameters<this[M]>
  ): ReturnType<this[M]> {
    const method = this[methodName] as Function
    const result = method.apply(this, args)
    return result
  }
}

export default new TrafficNetwork()
